﻿using System;
using Microsoft.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using Bling.DSL;
using Bling.Util;
using Bling.Core;
using System.Linq;

namespace Bling.Vecs {
  public abstract partial class Arity : IArity {
    public abstract Func<Expression, Expression> PropertyFor(int Idx);
  }
  public partial class Arity1<T> : Arity<T, T, Arity1<T>> {
    public override Func<Expression, Expression> PropertyFor(int Idx) {
      (Idx == 0).Assert();
      return e => e;
    }
  }

  public partial interface IMultiArity : IArity {
    Func<Expression, Expression> PropertyFor(int Idx);
  }

  public abstract partial class MultiArity<K, T, ARITY> : Arity<K, T, ARITY>, IMultiArity where ARITY : MultiArity<K, T, ARITY>, new() {
    public abstract string[] PropertyNames { get; }
    public override Func<Expression,Expression> PropertyFor(int Idx) {
      var p = typeof(T).GetProperty(PropertyNames[Idx]);
      (p != null).Assert();
      return e => Expression.Property(e, p);
    }
  }
  public partial class Vec2Arity<T> : Arity2<T, Vec2<T>, Vec2Arity<T>> {
    public override string[] PropertyNames {
      get { return new string[] { "X", "Y" }; }
    }
  }
  public partial class Vec3Arity<T> : Arity3<T, Vec3<T>, Vec3Arity<T>> {
    public override string[] PropertyNames {
      get { return new string[] { "X", "Y", "Z", }; }
    }
  }
  public partial class Vec4Arity<T> : Arity4<T, Vec4<T>, Vec4Arity<T>> {
    public override string[] PropertyNames {
      get { return new string[] { "X", "Y", "Z", "W" }; }
    }

  }

}
namespace Bling.Ops {
  using linq = Microsoft.Linq.Expressions;
  using Bling.Vecs;

  public partial class Sign : StaticCallOperator<double, double, Sign> {
    public override Func<Expression, Expression> Linq(Operation<double, double> op) {
      return null;
    }
  }
  public static partial class ValueOperators<T> {
    public partial class Equal : Ops.BinaryOperator<T, bool, Equal> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, bool> op) {
        return linq.Expression.Equal;
      }
    }
    public partial class NotEqual : Ops.BinaryOperator<T, bool, NotEqual> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, bool> op) {
        return linq.Expression.NotEqual;
      }
    }
    public partial class BaseCondition<OP> : Operator<bool, T, T, T, OP> where OP : BaseCondition<OP> {
      public override Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Operation<bool, T, T, T> op) {
        return linq.Expression.Condition;
      }
    }
  }
  public static partial class BoolOperators<T> {
    public partial class And : UniformBinaryOperator<T, bool, And> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<bool, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.And;
      }
    }
    public partial class Or : UniformBinaryOperator<T, bool, Or> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<bool, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Or;
      }
    }
    public partial class ExclusiveOr : UniformBinaryOperator<T, bool, ExclusiveOr> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<bool, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.ExclusiveOr;
      }
    }
    public partial class Not : UniformUnaryOperator<T, bool, Not> {
      public override Func<linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T> op) {
        if (Arity<bool, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Not;
      }
    }
  }
  public static partial class NumericOperators0<T, K> {
    public abstract partial class MathOperator<CNT> : Ops.StaticCallOperator<T, T, CNT> {
      public override Func<linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T> op) {
        if (Arity<K, T>.ArityI.Count != 1) return null;
        else return base.Linq(op);
      }
    }
    public partial class Clamp : Math3Operator<Clamp> {
      public override Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T, T> op) {
        return null;
      }
    }
    public partial class Negate : UniformUnaryOperator<T, K, Negate> {
      public override Func<linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Negate;
      }
    }
    public partial class Add : ArithmeticOperator<Add> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Add;
      }
    }
    public partial class Subtract : ArithmeticOperator<Subtract> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Subtract;
      }
    }
    public partial class Multiply : ArithmeticOperator<Multiply> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Multiply;
      }
    }
    public partial class Divide : ArithmeticOperator<Divide> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Divide;
      }
    }
    public partial class Modulo : ArithmeticOperator<Modulo> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, T> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.Modulo;
      }
    }
  }
  public static partial class NumericOperators<T, K, BT> {
    public partial class GreaterThan : ArithmeticOperator<GreaterThan> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, BT> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.GreaterThan;
      }
    }
    public partial class LessThan : ArithmeticOperator<LessThan> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, BT> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.LessThan;
      }
    }
    public partial class GreaterThanOrEqual : ArithmeticOperator<GreaterThanOrEqual> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, BT> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.GreaterThanOrEqual;
      }
    }
    public partial class LessThanOrEqual : ArithmeticOperator<LessThanOrEqual> {
      public override Func<linq.Expression, linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, T, BT> op) {
        if (Arity<K, T>.ArityI.Count > 1) return base.Linq(op);
        return linq.Expression.LessThanOrEqual;
      }
    }
  }
  public static partial class DoubleOperators<T, B> {
    public abstract partial class DoubleOperator<CNT> : StaticCallOperator<T, B, CNT> {
      public override Func<linq.Expression, linq.Expression> Linq(Bling.Ops.Operation<T, B> op) {
        if (Arity<double, T>.ArityI.Count > 1) return null;
        else return base.Linq(op);
      }
    }
  }
}

namespace Bling.Linq {
  using Bling.Vecs;
  public abstract partial class LinqEval<EVAL> : ScopedEval<EVAL>, ITableEval<EVAL>, IParameterEval<EVAL>, Ops.IConditionEval<EVAL>, IArrayEval<EVAL>, ILoopEval<EVAL, Expression>, IExpressionEval<EVAL, Expression>, IRandomEval<EVAL> where EVAL : Eval<EVAL> {
    public override Info<K> Access<K, T, ARITY>(Info<T> info, int Idx) {
      var p = MultiArity<K, T, ARITY>.ArityI.PropertyFor(Idx);
      Expression e = p((((MyInfo<T>)info).Value));
      if (e.Type != typeof(K)) e = Expression.Convert(e, TransformType(typeof(K)));
      var ret = new MyInfo<K>() { Value = e };
      AtLeast(ret, info);
      return ret;
    }
    public override Info<T> Composite<K, T>(IComposite<K, T> Expr, params Info<K>[] info) {
      var param = Expression.Parameter(typeof(T), "composite");
      (info.Length == Expr.ComponentCount).Assert();
      var body = new Expression[info.Length + 2];
      body[0] = Expression.Assign(param, Expression.New(TransformType(typeof(T))));
      for (int i = 0; i < info.Length; i++) {
        var p = Expr.PropertyFor(i);
        var ke = ((MyInfo<K>)info[i]).Value;
        var e = p(param);
        if (e.Type != typeof(K)) ke = Expression.Convert(ke, TransformType(e.Type));
        body[i + 1] = Expression.Assign(e, ke);
      }
      body[info.Length + 1] = param;
      var retE = Expression.Block(new ParameterExpression[] { param }, body);
      var retF = new MyInfo<T>() { Value = retE };
      foreach (var i in info) AtLeast(retF, i);
      return retF;
    }
  }
}